﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MoneyWentWhere.DataModel.Reports
{
    public abstract class TableReport
    {
        public readonly TimeSpanUnit PeriodUnit;


        protected Dictionary<PeriodAmountKey, PeriodAmount> _periodAmounts = new Dictionary<PeriodAmountKey, PeriodAmount>();

        // Column lookup
        protected List<TableReportColumn> _columns;
        protected Dictionary<TableReportColumn, int> _columnIndexes;

        public TableReport(TimeSpanUnit periodUnit)
        {
            PeriodUnit = periodUnit;
        }

        public void AddTransactions(Account acct)
        {
            foreach (Transaction tx in acct.Transactions)
            {
                if (tx.Amount == 0) { continue; }

                PeriodAmountKey key = new PeriodAmountKey(tx, PeriodUnit);
                PeriodAmount pa;
                if (!_periodAmounts.TryGetValue(key, out pa))
                {
                    pa = new PeriodAmount(key);
                    _periodAmounts.Add(key, pa);
                }
                pa.Add(tx);
            }
            // TODO: reco tx must be *distributed* if they overlap periods. More difficult.
            // Perhaps leave this down to specific reports to do
        }

        /// <summary>
        /// Create all report columns, and _columnIndexes 
        /// </summary>
        /// <returns></returns>
        List<TableReportColumn> CreateColumns()
        {
            Dictionary<TableReportColumn, object> uniqueCols = new Dictionary<TableReportColumn, object>();

            foreach (PeriodAmount pa in _periodAmounts.Values)
            {
                TableReportColumn col = CreateColumn(pa);
                if (!uniqueCols.ContainsKey(col)) { uniqueCols.Add(col, null); }
            }

            _columns = new List<TableReportColumn>();
            _columns.AddRange(uniqueCols.Keys);
            _columns.Sort();

            // Save column indexes for quick lookup
            _columnIndexes = new Dictionary<TableReportColumn, int>();
            for (int i = 0; i < _columns.Count; i++) { _columnIndexes.Add(_columns[i], i); }

            return _columns;
        }

        /// <summary>
        /// Looks up the column in a hashtable.
        /// To use, populate _columnIndexes hashtable and override CreateLookupColumn method
        /// </summary>
        /// <param name="pa"></param>
        /// <returns></returns>
        int FindColumnIndex(PeriodAmount pa)
        {
            TableReportColumn lookupCol = CreateColumn(pa);
            // lookupCol should be present, OK to throw if not
            return _columnIndexes[lookupCol];
        }

        /// <summary>
        /// Create a temporary column used for lookup in the indexes hashtable
        /// </summary>
        /// <param name="pa"></param>
        /// <returns></returns>
        protected abstract TableReportColumn CreateColumn(PeriodAmount pa);


        public void PrettyPrint()
        {
            // Sort the period amounts by time
            Console.WriteLine("Count: " + _periodAmounts.Count);
            List<PeriodAmount> list = new List<PeriodAmount>(_periodAmounts.Values);
            list.Sort();

            _columns = CreateColumns();

            Dictionary<DateTime, TableReportRow> rows = new Dictionary<DateTime, TableReportRow>();
            foreach (PeriodAmount pa in list)
            {
                TableReportRow row;
                if (!rows.TryGetValue(pa.Key.StartDate, out row))
                {
                    row = new TableReportRow(pa.Key.StartDate, pa.Key.DurationUnit, _columns.Count);
                    rows.Add(pa.Key.StartDate, row);
                }
                int colIndex = FindColumnIndex(pa);
                row.AddPeriodAmount(pa, colIndex);
            }

            // sort the rows
            List<TableReportRow> rowList = new List<TableReportRow>(rows.Values);
            rowList.Sort();

            PrintColumnHeaders();
            foreach (TableReportRow row in rowList)
            {
                Console.WriteLine(row);
            }
        }

        void PrintColumnHeaders()
        {
            // Output
            for (int i = 0; i < _columns.Count; i++)
            {
                Console.WriteLine(i + ") " + _columns[i]);
            }
            Console.WriteLine();

            Console.Write(Field("Start"));
            for (int i = 0; i < _columns.Count; i++) 
            { 
                Console.Write(Field(i.ToString())); 
            }
            Console.WriteLine();

            Console.Write(Field(""));
            for (int i = 0; i < _columns.Count; i++) 
            {
                Console.Write(Field(
                    _columns[i].Key.Account.Name)); 
            }
            Console.WriteLine();

            Console.Write(Field(""));
            for (int i = 0; i < _columns.Count; i++) 
            {
                Console.Write(Field(
                    (_columns[i].Key.IsDeposit ? "+" : "-") + 
                    _columns[i].Key.Kind.ToString() )); 
            }
            Console.WriteLine();

            Console.Write(Field(""));
            for (int i = 0; i < _columns.Count; i++)
            {
                Console.Write(Field(
                    _columns[i].Key.Category));
            }
            Console.WriteLine();
        }

        String Field(String s)
        {
            if (s.Length > TableReportRow.ColWidth) { return s.Substring(0, TableReportRow.ColWidth) + "|"; }
            return s.PadRight(TableReportRow.ColWidth) + "|";
        }
    }



}
